﻿using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using CanvasControl.Model;
using CanvasControl.View;
using GraphDrawing.Graph.Model;

namespace GraphDrawing.Graph.View
{
    public abstract class SlotView
    {
        public Appearance Default { get; private set; }
        public Appearance Pressed { get; private set; }
        public Appearance Acceptable { get; private set; }
        public Appearance Unacceptable { get; private set; }

        protected SlotView()
        {
            Default = new Appearance();
            Pressed = new Appearance();
            Acceptable = new Appearance();
            Unacceptable = new Appearance();
        }

        /// <summary>
        /// Рисует слот с использованием переданного контекста.
        /// </summary>
        /// <param name="dc">Контекст рисования.</param>
        /// <param name="slotModel">Модель отображаемого слота.</param>
        public abstract void Draw(DrawingContext dc, ViewPortModel viewPort, SlotModel slotModel);

        protected Appearance GetStateAppearance(SlotState state)
        {
            switch (state)
            {
                case SlotState.DEFAULT:
                    return Default;
                case SlotState.PRESSED:
                    return Pressed;
                case SlotState.ACCEPTABLE:
                    return Acceptable;
                case SlotState.UNACCEPTABLE:
                    return Unacceptable;
                default:
                    return Default;
            }
        }
    }

    public class CircleSlotView : SlotView
    {
        public override void Draw(DrawingContext dc, ViewPortModel viewPort, SlotModel slotModel)
        {
            var g = dc.Graphics;
            var temp = dc.Graphics.BeginContainer();
            try
            {
                var appearance = GetStateAppearance(slotModel.state);
                var shape = (CircleShape) slotModel.Shape;
                var pos = slotModel.Parent.position.Move(slotModel.ParentDisposition);
                var clientRect = viewPort.GlobalToClient(new Rectangle(pos, new Size(shape.radius*2, shape.radius*2)));
                using (var brush = appearance.CreateBrush())
                    g.FillEllipse(brush, clientRect);
                g.SmoothingMode = SmoothingMode.AntiAlias;
                using (var pen = appearance.Border.CreatePen())
                    g.DrawEllipse(pen, clientRect);
            }
            finally
            {
                g.EndContainer(temp);
            }
        }
    }

    public class AntennaSlotView : SlotView
    {
        readonly Model model;

        public AntennaSlotView(Direction direction, int connectorLength, CapShape capShape, int width, int height)
        {
            model = new Model(direction, connectorLength, capShape, width, height);
        }

        public override void Draw(DrawingContext dc, ViewPortModel viewPort, SlotModel slotModel)
        {
            var g = dc.Graphics;
            var temp = dc.Graphics.BeginContainer();
            try
            {
                var appearance = GetStateAppearance(slotModel.state);
                var joint = new Point();
                var connectorEndPoint = GetConnectorEndPoint(joint);
                using (var pen = appearance.Border.CreatePen())
                {
                    g.DrawLine(pen, joint, connectorEndPoint);
                    using (var brush = new SolidBrush(appearance.Color))
                        DrawShape(dc, brush, pen, connectorEndPoint);
                }
            }
            finally
            {
                g.EndContainer(temp);
            }
        }

        void DrawShape(DrawingContext dc, Brush backgroundBrush, Pen borderPen, Point connectorEndPoint)
        {
            var g = dc.Graphics;
            g.SmoothingMode = SmoothingMode.AntiAlias;
            var clientRect = new Rectangle(); //TODO : calculate size
            switch (model.capShape)
            {
                case CapShape.CIRCLE:
                    g.FillEllipse(backgroundBrush, clientRect);
                    g.DrawEllipse(borderPen, clientRect);
                    break;
                case CapShape.RECTANGLE:
                    g.FillRectangle(backgroundBrush, clientRect);
                    g.DrawEllipse(borderPen, clientRect);
                    break;
                case CapShape.DIAMOND:
                    var delta = 0;
                    var path = new GraphicsPath();
                    path.AddLines(new[]
                    {
                        connectorEndPoint, new Point(connectorEndPoint.X - delta, connectorEndPoint.Y + delta), new Point(connectorEndPoint.X - 2*delta, connectorEndPoint.Y),
                        new Point(connectorEndPoint.X - delta, connectorEndPoint.Y - delta), connectorEndPoint
                    });
                    g.FillPath(backgroundBrush, path);
                    g.DrawPath(borderPen, path);
                    break;
            }
        }

        Point GetConnectorEndPoint(Point begin)
        {
            var length = model.connectorLength;
            switch (model.direction)
            {
                case Direction.LEFT:
                case Direction.RIGHT:
                    return new Point(begin.X + (model.direction == Direction.LEFT ? -length : length), begin.Y);
                case Direction.TOP:
                case Direction.BOTTOM:
                    return new Point(begin.X, begin.Y + (model.direction == Direction.TOP ? -length : length));
            }
            throw new InvalidOperationException();
        }

        /// <summary>
        /// Направление антенны.
        /// </summary>
        public enum Direction
        {
            LEFT,
            TOP,
            RIGHT,
            BOTTOM
        }

        /// <summary>
        /// Форма наконечника антенны.
        /// </summary>
        public enum CapShape
        {
            CIRCLE,
            RECTANGLE,
            DIAMOND
        }

        /// <summary>
        /// Модель фигуры антенны.
        /// </summary>
        class Model : IShape
        {
            readonly Point joint;
            readonly Rectangle hitArea;

            readonly int capWidth;
            readonly int capHeight;

            public readonly CapShape capShape;
            public readonly Direction direction;
            public readonly int connectorLength;

            public Model(Direction direction, int connectorLength, CapShape capShape, int capWidth, int capHeight)
            {
                this.direction = direction;
                this.connectorLength = connectorLength;
                this.capShape = capShape;
                this.capWidth = capWidth;
                this.capHeight = capHeight;
                joint = CalculateJoint();
                hitArea = CalculateHitArea();
            }

            public Point Joint
            {
                get { return joint; }
            }

            public Rectangle HitArea
            {
                get { return hitArea; }
            }

            public bool PreciseHitTest(Point point)
            {
                return hitArea.Contains(point);
            }

            Point CalculateJoint()
            {
                switch (direction)
                {
                    case Direction.LEFT:
                        return new Point(connectorLength + capWidth, capHeight/2);
                    case Direction.RIGHT:
                        return new Point(0, capHeight/2);
                    case Direction.TOP:
                        return new Point(capWidth/2, connectorLength + capHeight);
                    case Direction.BOTTOM:
                        return new Point(capWidth/2, 0);
                }
                throw new InvalidOperationException();
            }

            Rectangle CalculateHitArea()
            {
                switch (direction)
                {
                    case Direction.LEFT:
                        return new Rectangle(0, 0, capWidth, capHeight);
                    case Direction.RIGHT:
                        return new Rectangle(connectorLength, 0, connectorLength + capWidth, capHeight);
                    case Direction.TOP:
                        return new Rectangle(0, connectorLength + capHeight, capWidth, connectorLength);
                    case Direction.BOTTOM:
                        return new Rectangle(0, connectorLength, capWidth, connectorLength + capHeight);
                }
                throw new InvalidOperationException();
            }
        }
    }
}